package com.free.mini.gateway.utils;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import java.util.TreeMap;

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.free.mini.gateway.utils.encryption.MD5UtilsCash;
import org.bouncycastle.jcajce.provider.digest.MD5;
import org.springframework.util.Base64Utils;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 *  验签
 */
@Slf4j
public class SignUtil {


	private static final String encoding = "UTF-8";

	/**
	 * 加密算法RSA
	 */
	public static final String KEY_ALGORITHM = "RSA";
	/**
	 * 签名算法 SHA256WithRSA
	 */
	public static final String SIGNATURE_ALGORITHM = "SHA256WithRSA";
	private static String KEY = "密钥";

	private static String IV = "向量";
	private static final String SECRET = "AES";
	private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";

	/**
	 * 验证签名
	 *
	 * @param  treeMap request data
	 * @return
	 */
	public static boolean verify(Map<String,Object> treeMap) {
		String requestSign = (String) treeMap.getOrDefault("signature","");
		treeMap.remove("signature");
		String signParamsMd5 = cn.hutool.crypto.SignUtil.signParamsMd5(treeMap);
		if (signParamsMd5.equals(requestSign)){
			log.warn("signUtil verify验签异常={} requestSign={} requestData={}",signParamsMd5,requestSign);
			return false;
		}
		return true;
	}

	/**
	 * 加签
	 * @return
	 */
	public static String sign(Object o) {
		return MD5UtilsCash.getMD5(JSON.toJSONString(o, SerializerFeature.MapSortField));
//		return cn.hutool.crypto.SignUtil.signParamsMd5(map);
	}

	/**
	 * AES秘钥key生成
	 * @return key
	 */
	public static String generateAESKey(){
		return generateAESKey(128);
	}
	/**
	 * AES秘钥key生成
	 * @param keySize key长度 默认128
	 * @return key
	 */
	public static String generateAESKey(Integer keySize){
		byte[] encoded = KeyUtil.generateKey(SymmetricAlgorithm.AES.getValue(), keySize).getEncoded();
		return Base64.encode(encoded);
	}

	/**
	 * aes加密
	 * @param iv iv
	 * @param key 加密key
	 * @param data 加密数据
	 * @return
	 */
	public static String aesEncrypt(String iv, String key, String data){
//		return aes(iv,key).encryptBase64(data);
		try {
			Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
			byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
			IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
			cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, SECRET),ivspec);
			byte[] doFinal = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
			return new String(java.util.Base64.getEncoder().encode(doFinal));
		}catch (Exception e){
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * aes解密
	 * @param iv iv
	 * @param key 加密key
	 * @param data 加密数据
	 * @return
	 */
	public static String aesDecrypt(String iv, String key, String data){
//		return aes(iv,key).decryptStr(data);
		try {
			Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
			byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
			IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
			cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, SECRET),ivspec);
			byte[] doFinal = cipher.doFinal(java.util.Base64.getDecoder().decode(data));
			return new String(doFinal);
		}catch (Exception e){
			e.printStackTrace();
			return null;
		}
	}
	/**
	 * aes解密
	 * @param iv iv
	 * @param key 加密key
	 * @return
	 */
	public static AES aes(String iv, String key){


		return new AES(Mode.CBC, Padding.PKCS5Padding,
				Base64.decode(key), Base64.decode(iv));
	}
	/**
	 * AES加密ECB模式PKCS7Padding填充方式
	 * @param str 字符串
	 * @param key 密钥
	 * @return 加密字符串
	 * @throws Exception 异常信息
	 */
	public static String aesPkcs7Encrypt(String str, String key,String iv) throws Exception {
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
		IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
		cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, SECRET),ivspec);
		byte[] doFinal = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));
		return new String(java.util.Base64.getEncoder().encode(doFinal));
	}

	/**
	 * AES解密ECB模式PKCS7Padding填充方式
	 * @param str 字符串
	 * @param key 密钥
	 * @return 解密字符串
	 * @throws Exception 异常信息
	 */
	public static String aes2Pkcs7PaddingDecrypt(String str, String key,String iv) throws Exception {
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
		IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
		cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, SECRET),ivspec);
		byte[] doFinal = cipher.doFinal(java.util.Base64.getDecoder().decode(str));
		return new String(doFinal);
	}
	/**
	 * rsa私钥解密
	 * @param privateKey 私钥
	 * @param data 要解密的数据
	 * @return 明文
	 */
	public static String rsaPrivateKeyDecrypt(String privateKey, String data){
		return SecureUtil.rsa(Base64.decode(privateKey),null)
				.decryptStr(data, KeyType.PrivateKey);
	}

	/**
	 * rsa 私钥加密
	 * @param privateKey 私钥
	 * @param data 要加密的数据
	 * @return 密文
	 */
	public static String rsaPrivateKeyEncrypt(String privateKey, String data){
		return SecureUtil.rsa(Base64.decode(privateKey),null)
				.encryptBase64(data, KeyType.PrivateKey);
	}

	/**
	 * rsa公钥解密
	 * @param data 要解密的数据
	 * @return 明文
	 */
	public static String rsaPublicKeyDecrypt(String publicKey, String data){
		return SecureUtil.rsa(null,Base64.decode(publicKey))
				.decryptStr(data, KeyType.PublicKey);
	}

	/**
	 * rsa 公钥加密
	 * @param data 要加密的数据
	 * @return 密文
	 */
	public static String rsaPublicKeyEncrypt(String publicKey, String data){
		return SecureUtil.rsa(null,Base64.decode(publicKey))
				.encryptBase64(data, KeyType.PublicKey);
	}
}
